<?php
/**
 * Created by PhpStorm.
 * User: ari
 * Date: 10/13/14
 * Time: 3:44 PM
 */
namespace BC\Games\Holdem\Hands;

use BC\Games\Holdem\Cards;
use BC\Games\Holdem\Deck;
use CPath\UnitTest\ITestable;
use CPath\UnitTest\IUnitTestRequest;

class ThreeOfAKindHand implements IPokerHand, ITestable
{
	const RANK = 3000;

	private $mSet;
	private $mKickers;
	public function __construct(Array $set, Array $kickers) {
		$this->mSet = $set;
		$this->mKickers = $kickers;
	}

	/**
	 * Return name of this hand
	 * @return String
	 */
	function getName() {
		return "Three of a Kind";
	}

	function getCards($withNullDelimiter=null) {
		$cards = array_merge(
			$this->mSet,
			array(null),
			$this->mKickers
		);
		return $withNullDelimiter ? $cards : array_filter($cards);
	}

	/**
	 * Get Hand rank
	 * @return int
	 */
	function getRank() {
		return self::RANK
		+ ($this->mSet[0] & Cards::NUMBER_MASK)
		+ ($this->mKickers[0] & Cards::NUMBER_MASK)/100
		+ ($this->mKickers[1] & Cards::NUMBER_MASK)/100;
	}

	/**
	 * Compare two hands
	 * @param IPokerHand $Hand
	 * @return int 0, <0, or >0
	 */
	function compare(IPokerHand $Hand) {
		$rank = $this->getRank() - $Hand->getRank();
		if(!$Hand instanceof ThreeOfAKindHand)
			return $rank;

		$rank = $this->mSet[0] - $Hand->mSet[0];
		if($rank !== 0)
			return $rank;

		foreach($this->mKickers as $i => $kicker) {
			$rank = $kicker - $Hand->mKickers[$i];
			if($rank !== 0)
				return $rank;
		}

		return 0;
	}

	/**
	 * Returns a string representation of this poker hand
	 * @return String
	 */
	function __toString() {
		return
			"[" . Cards::printCards($this->mSet[0])
			. ", " . Cards::printCards($this->mSet[1])
			. ", " . Cards::printCards($this->mSet[2])
			. "], " . Cards::printCards($this->mKickers[0])
			. ", " . Cards::printCards($this->mKickers[1]);
	}

	// Static

	/**
	 * Tests a poker hand to see if it matches an array of cards
	 * @param array $cards
	 * @param IPokerHand[] $Matches
	 * @return bool true if the set of cards matches this poker hand
	 */
	static function match(Array $cards, &$Matches = array()) {

		$matches = array();
		foreach ($cards as $card) {
			$num = $card & Cards::NUMBER_MASK;
			if (isset($matches[$num])) {
				$matches[$num][] = $card;

			} else {
				$matches[$num] = array($card);
			}
		}

		$sets = array();
		foreach($matches as $num => $match) {
			if(sizeof($match) === 3)
				$sets[] = $match;
		}

		if(sizeof($sets) === 0)
			return false;

		if(sizeof($sets) > 1) {
			usort($sets, function($trip1, $trip2) {
				return $trip1[0] - $trip2[0];
			});
		}

		$set = $sets[0];

		$kickers = array_diff($cards, $set);
		rsort($kickers);
		if(sizeof($kickers) > 2)
			$kickers = array_slice($kickers, 0, 2);

		$Matches[] = new ThreeOfAKindHand($set, $kickers);
		return true;
	}

	// Static

	/**
	 * Perform a unit test
	 * @param IUnitTestRequest $Test the unit test request inst for this test session
	 * @return void
	 * @test --disable 0
	 * Note: Use doctag 'test' with '--disable 1' to have this ITestable class skipped during a build
	 */
	static function handleStaticUnitTest(IUnitTestRequest $Test) {
		$Deck = new Deck();
		$Deck->replay(Cards::ACE | Cards::HEARTS, 'player1');
		$Deck->replay(Cards::FIVE | Cards::CLUBS, 'player1');
		$Deck->replay(Cards::ACE | Cards::CLUBS, 'board');
		$Deck->replay(Cards::SEVEN | Cards::HEARTS, 'board');
		$Deck->replay(Cards::THREE | Cards::SPADES, 'board');
		$Deck->replay(Cards::TWO | Cards::CLUBS, 'board');
		$Deck->replay(Cards::ACE | Cards::DIAMONDS, 'board');

		$cards = $Deck->getDrawnCards('player1', 'board');
		if(!ThreeOfAKindHand::match($cards, $Hands))
			$Test->fail("Three of a kind not matched to cards: " . Cards::printCards($cards));

		$Test->log("Matched Three of a Kind to " . $Hands[0] . ' from ' . Cards::printCards($cards), $Test::VERBOSE);
	}
}

